import { TinyImport } from './base'

export class RouteImport extends TinyImport {
  constructor() {
    super({
      exclude: 'index',
    })
  }

  importRoute({ root, ...params }) {
    const requireObj = this.importVue({
      exclude: /^_[\s\S]*/,
      key: ({ path }) => this._formatPath(path),
      value: ({ file }) => file.default,
      ...params,
    })

    const routes = this._normalizeRoute(requireObj)

    if (!this._inType(root, [undefined, ''])) {
      throw new Error('[RouteImport] root expected string!')
    }

    if (root && requireObj[root]) {
      routes.unshift({
        path: '/',
        name: '/',
        component: requireObj[root],
      })
    }

    return routes
  }

  importComponent(params) {
    return this.importVue({
      noSub: true,
      include: /^_[\s\S]*/,
      key: ({ name }) => name.replace('_', ''),
      value: ({ file }) => file.default,
      ...params,
    })
  }

  _formatPath(path) {
    path = path.replace('/index.vue', '.vue')
    path = path.replace('.vue', '')
    path = path.replace('./', '')

    return path
  }

  _normalizePath(path, isSub) {
    if (path.indexOf('@') !== -1) return '*'

    const dynamicNameIndex = path.indexOf('#')
    if (dynamicNameIndex !== -1) path = path.substr(0, dynamicNameIndex)

    path = (isSub ? '' : '/') + path
    path = path.toLowerCase()

    return path
  }

  _normalizeName(name) {
    const dynamicNameIndex = name.indexOf('#')
    if (dynamicNameIndex !== -1) return name.substr(dynamicNameIndex + 1)

    name = name.replace(':', '')

    return name
  }

  // mv child route into children obj
  _normalizeRoute(routeObj) {
    const result = []

    for (const path in routeObj) {
      const pathArray = path.split('/')
      const isSub = pathArray.length > 1

      const checkInsertToParent = (_parent, _path) => {
        const _dir = _path.split('/').slice(-1)[0]
        const _isSub = _path.indexOf('/') !== -1
        const _isDaynamic = _path.indexOf(':') !== -1
        const _isEmptyMatch = _path.indexOf('@') !== -1

        if (_parent.some(s => s._originPath === _dir)) return

        _parent.push({
          path: this._normalizePath(_dir, _isSub),
          name: this._normalizeName(_dir),
          component: routeObj[_path],
          children: [],
          _originPath: _dir,
          _order: _isEmptyMatch ? 2 : _isDaynamic ? 1 : 0,
        })
      }

      let parent = result

      if (isSub) {
        // if is sub, check it parent is set
        pathArray.slice(0, pathArray.length - 1).map((dir, key) => {
          // check it is set at parent, if not, set it
          const parentDir = key === 0 ? parent : parent.children
          checkInsertToParent(parentDir, pathArray.slice(0, key + 1).join('/'))

          // set parent
          parent = parentDir.filter(s => s._originPath === dir)[0].children
        })
      }

      // then set current path into parent
      checkInsertToParent(parent, path)
    }

    return this._sortRoute(result)
  }

  // less deep route will be place at first
  // dynamic route will be place at last
  _sortRoute(routeList) {
    routeList.sort((a, b) => a._order - b._order)

    routeList.forEach(route => {
      if (route.children) {
        this._sortRoute(route.children)
      }
    })

    return routeList
  }
}

export const routeImport = new RouteImport()
